package com.lsh.ofc.worker.task;

import com.alibaba.fastjson.JSONObject;
import com.dangdang.ddframe.job.api.JobExecutionMultipleShardingContext;
import com.dangdang.ddframe.job.plugin.job.type.simple.AbstractSimpleElasticJob;
import com.google.common.collect.Lists;
import com.lsh.base.common.exception.BusinessException;
import com.lsh.base.common.json.JsonUtils2;
import com.lsh.ofc.core.constant.Constants;
import com.lsh.ofc.core.entity.OfcOrderHead;
import com.lsh.ofc.core.entity.OfcSoHead;
import com.lsh.ofc.core.entity.OfcTask;
import com.lsh.ofc.core.enums.*;
import com.lsh.ofc.core.redis.RedisTemplate;
import com.lsh.ofc.core.service.OfcOrderService;
import com.lsh.ofc.core.service.OfcSoService;
import com.lsh.ofc.core.service.OfcTaskService;
import com.lsh.ofc.core.util.IdGenerator;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * Project Name: lsh-ofc
 * @author fuhao
 * Date: 18/6/19
 * Time: 18/6/19.
 * 北京链商电子商务有限公司
 * Package name:com.lsh.ofc.worker.task.
 * desc:定时任务，汇总sto
 */
@Slf4j
@Component
public class OfcStoTaskCreateJob extends AbstractSimpleElasticJob {

    @Autowired
    private OfcOrderService ofcOrderService;

    @Autowired
    private OfcTaskService ofcTaskService;

    @Autowired
    private OfcSoService ofcSoService;

    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    public void process(JobExecutionMultipleShardingContext jobContext) {
        // 默认100
        try {
            log.info("ofc_task sto task create 开始...");
            long start = System.currentTimeMillis();
            List<OfcOrderHead> orders = new ArrayList<>();
            int defaultSize = this.getCollectOrder(jobContext, orders);
            // 前置仓+机构 与 solist 关系map
            Map<String, List<String>> sbcMap = new HashMap<>(500);
            //订单号与订单信息关系Map 全局
            Map<String, OfcOrderHead> orderCode2OrderMap = new HashMap<>(orders.size());
            //soBillCode与订单号关系Map 全局
            Map<String, String> soBillCode2orderCodeMap = new HashMap<>(orders.size() * 2);
            //订单号与STO关系Map 全局
            Map<String, List<String>> orderCode2stoMap = new HashMap<>(orders.size());
            for (OfcOrderHead order : orders) {
                //去重已经汇总的sto
                String orderStoKey = Constants.OFC_STO_ORDER + order.getOrderCode();
                if (redisTemplate.exists(orderStoKey)) {
                    continue;
                }
                orderCode2OrderMap.put(order.getOrderCode().toString(), order);
                OfcSoHead soFilter = new OfcSoHead();
                soFilter.setOrderCode(order.getOrderCode());
                List<OfcSoHead> sos = this.ofcSoService.findList(soFilter, false);

                if (CollectionUtils.isEmpty(sos)) {
                    log.error("SO信息不存在！订单号=" + order.getOrderCode());
                }
                for (OfcSoHead ofcSoHead : sos) {
                    /* 按照前置仓ID+DC+货主拆sto任务  */
                    String stoKey = ofcSoHead.getPreWarehouseCode() + ofcSoHead.getSupplierDc() + ofcSoHead.getSupplierOrg();

                    List<String> sbcList = sbcMap.get(stoKey);
                    if (null == sbcList) {
                        sbcList = new ArrayList<>();
                        sbcMap.put(stoKey, sbcList);
                    }
                    //需要按照task 清空
                    sbcList.add(ofcSoHead.getSoBillCode());
                    //全局sobillcode与 orderCode 关系map
                    soBillCode2orderCodeMap.put(ofcSoHead.getSoBillCode(), ofcSoHead.getOrderCode().toString());

                    if (sbcList.size() >= defaultSize) {
                        this.order2stoRelationByTask(sbcList, soBillCode2orderCodeMap, orderCode2stoMap);
                    }
                }
            }

            for (Map.Entry<String, List<String>> entrySet : sbcMap.entrySet()) {
                List<String> sbcList = entrySet.getValue();
                this.order2stoRelationByTask(sbcList, soBillCode2orderCodeMap, orderCode2stoMap);
            }

            this.addOrderAndStoRelation(orderCode2OrderMap, orderCode2stoMap);

            long end = System.currentTimeMillis();
            log.info("ofc_task sto task create 完成... 处理行数：" + orders.size() + "，耗时：" + (end - start));
        } catch (BusinessException e) {
            log.error("ofc_task sto task create 异常... " + e.getMessage(), e);
        }
    }

    /**
     * 查询需要汇总的订单
     * @param jobContext   任务上线文
     * @param orders       需要汇总的订单列表
     * @return             汇总sto指定大小
     */
    private Integer getCollectOrder(JobExecutionMultipleShardingContext jobContext, List<OfcOrderHead> orders) {

        String exeArgs = jobContext.getJobParameter();
        exeArgs = StringUtils.isNotBlank(exeArgs) ? exeArgs : "22:30;22:30;20";
        String[] exeTimeArr = exeArgs.split(";");
        String startTimePoint = exeTimeArr[0];
        String endTimePoint = exeTimeArr[1];
        int defaultSize = Integer.parseInt(exeTimeArr[2]);
        String[] startTimePointArr = startTimePoint.split(":");
        String[] endTimePointArr = endTimePoint.split(":");
        Date nowDate = new Date();
        Integer startTime = this.getStartTime(nowDate, Integer.parseInt(startTimePointArr[0]), Integer.parseInt(startTimePointArr[1]));
        Integer endTime = this.getEndTime(nowDate, Integer.parseInt(endTimePointArr[0]), Integer.parseInt(endTimePointArr[1]));

        List<OfcOrderHead> ordersDb = ofcOrderService.findListFilterByTime(startTime, endTime, Region.BEIJING_CG.getCode(), DistributionWay.SEED_2_SHOP.getValue());
        log.info("[sto 汇总时间] 统计范围 {} - {} ,size = " + orders.size(), startTime, endTime);
        orders.addAll(ordersDb);

        return defaultSize;
    }

    /**
     * 汇总sto task及相关关系数据整理
     * @param sbcList       某个sto的列表
     * @param soBillCode2orderCodeMap   soCode与orderCode 关系
     * @param orderCode2stoMap          orderCode与sto 关系
     */
    private void order2stoRelationByTask(List<String> sbcList, Map<String, String> soBillCode2orderCodeMap, Map<String, List<String>> orderCode2stoMap) {
        OfcTask ofcTask = new OfcTask();
        // sto_code
        ofcTask.setRefId(IdGenerator.genId());
        ofcTask.setContent(JsonUtils2.obj2Json(sbcList));
        ofcTask.setType(OfcTaskType.STO_CREATE.getValue());
        ofcTask.setStatus(OfcTaskStatus.NEW.getValue());
        ofcTaskService.addTask(ofcTask);

        for (String soBillCode : sbcList) {
            String orderCode = soBillCode2orderCodeMap.get(soBillCode);
            // 标记redis 订单已处理
            this.setRedisDealFlag(orderCode);
            // 汇总 订单与sto的关系
            List<String> order2stoList = orderCode2stoMap.get(orderCode);
            if (CollectionUtils.isEmpty(order2stoList)) {
                order2stoList = new ArrayList<>();

                orderCode2stoMap.put(orderCode, order2stoList);
            }
            order2stoList.add(ofcTask.getRefId().toString());
        }
        // 清空已汇总数据
        sbcList.clear();
    }

    /**
     * 添加订单与sto的关系数据
     * @param orderCode2OrderMap orderCode与orderinfo的关系
     * @param orderCode2stoMap   orderCode与sto 关系
     */
    private void addOrderAndStoRelation(Map<String, OfcOrderHead> orderCode2OrderMap, Map<String, List<String>> orderCode2stoMap) {

        List<OfcTask> relationTaskList = Lists.newArrayList();
        Integer time = (int) (System.currentTimeMillis() / 1000);
        for (Map.Entry<String, List<String>> setEntry : orderCode2stoMap.entrySet()) {

            OfcOrderHead orderHead = orderCode2OrderMap.get(setEntry.getKey());
            String addressCode = orderHead.getAddressCode().toString();

            OfcTask relationTask = new OfcTask();
            // sto_code
            JSONObject content = new JSONObject();
            content.put("sto_codes", setEntry.getValue());
            content.put("address_code", addressCode);

            relationTask.setRefId(orderHead.getOrderCode());
            relationTask.setContent(content.toJSONString());
            relationTask.setType(OfcTaskType.STO_ORDER_SO_CREATE.getValue());
            relationTask.setStatus(OfcTaskStatus.NEW.getValue());
            relationTask.setExecCount(0);
            relationTask.setRemark("0");
            relationTask.setValid(Valid.enable.getValue());
            relationTask.setCreateTime(time);
            relationTask.setUpdateTime(time);

            relationTaskList.add(relationTask);

            if (relationTaskList.size() >= 1000) {
                if(ofcTaskService.addTasks(relationTaskList)){
                    // 清空任务列表
                    relationTaskList.clear();
                }
            }
        }

        if (relationTaskList.size() > 0) {
            ofcTaskService.addTasks(relationTaskList);
        }
    }


    /**
     * 设置汇总redis标记
     *
     * @param orderCode 订单号
     */
    private void setRedisDealFlag(String orderCode) {

        redisTemplate.set(Constants.OFC_STO_ORDER + orderCode, orderCode, 864000);
    }


    /**
     * 时间戳
     *
     * @param date        日期
     * @param hourPoint   小时
     * @param minutePoint 分钟
     * @return 时间戳
     */
    private Integer getStartTime(Date date, int hourPoint, int minutePoint) {

        return (int) TimeUnit.MILLISECONDS.toSeconds(this.getPreviousDayAndHourMorning(date, hourPoint, minutePoint).getTime());
    }

    /**
     * 统计结束时间
     *
     * @param hourPoint 时间点
     * @return 时间戳
     */
    private Integer getEndTime(Date date, int hourPoint, int minutePoint) {

        return (int) TimeUnit.MILLISECONDS.toSeconds(this.getDayHourMorning(date, hourPoint, minutePoint).getTime());
    }

    private Date getPreviousDayAndHourMorning(Date date, int hourPoint, int minutePoint) {

        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.set(Calendar.HOUR_OF_DAY, hourPoint);
        calendar.set(Calendar.MINUTE, minutePoint);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        calendar.add(Calendar.DATE, -1);

        return calendar.getTime();
    }

    private Date getDayHourMorning(Date date, int hourPoint, int minutePoint) {

        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.set(Calendar.HOUR_OF_DAY, hourPoint);
        calendar.set(Calendar.MINUTE, minutePoint);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);

        return calendar.getTime();
    }
}
